package fudan.se.project.service.advanced.conference.action;

import fudan.se.project.controller.request.DatesModifyRequest;
import fudan.se.project.controller.request.OnlyIdPropertyRequest;
import fudan.se.project.controller.request.PcMemberInviteRequest;
import fudan.se.project.controller.request.ReviewStartRequest;
import fudan.se.project.controller.response.BaseResponse;
import fudan.se.project.controller.response.UsersGetResponse;
import fudan.se.project.domain.User;
import fudan.se.project.domain.conference.Conference;
import fudan.se.project.domain.conference.Conference.AllocationStrategy;
import fudan.se.project.domain.conference.Conference.ConferenceStage;
import fudan.se.project.domain.conference.Conference.ConferenceState;
import fudan.se.project.service.infrastructure.entity.UserService;
import fudan.se.project.service.infrastructure.entity.conference.ConferenceService;
import fudan.se.project.service.infrastructure.entity.conference.production.ReviewCopyService;
import fudan.se.project.service.infrastructure.entity.conference.production.ReviewInfoService;
import fudan.se.project.service.infrastructure.func.conference.action.chair.ConferenceDatesModifyService;
import fudan.se.project.service.infrastructure.func.conference.action.chair.PcMemberInviteService;
import fudan.se.project.service.infrastructure.func.conference.resoure.paperalloc.PaperAllocateService;
import fudan.se.project.util.date.DateUtils;
import fudan.se.project.util.http.ResponseWrapperUtil;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.Date;
import java.util.List;

@Service
@RequiredArgsConstructor
public class ChairActionService {

    @NonNull
    private final ResponseWrapperUtil responseWrapperUtil;
    @NonNull
    private final DateUtils dateUtils;

    @NonNull
    private final ConferenceService conferenceService;
    @NonNull
    private final UserService userService;
    @NonNull
    private final ReviewInfoService reviewInfoService;

    @NonNull
    private final PcMemberInviteService pcMemberInviteService;

    @NonNull
    private final ConferenceDatesModifyService conferenceDatesModifyService;
    @NonNull
    private final PaperAllocateService paperAllocateService;
    @NonNull
    private final ReviewCopyService reviewCopyService;

    private static final String ARGUMENTS_ERROR = "arguments error";
    private static final String SUCCESS = "success";


    @PreAuthorize("hasAuthority('USER') and hasPermission(null, 'conference:chair:'+#id)")
    public ResponseEntity<?> searchPcMembers(long id, String realname) {

        Conference conference = conferenceService.findById(id);
        if (conference == null) {
            BaseResponse baseResponse = new BaseResponse(ARGUMENTS_ERROR);
            return responseWrapperUtil.buildResponseBody(baseResponse, HttpStatus.NOT_ACCEPTABLE);
        } else {
            List<User> userList = userService.findPcMemberCandidates(realname, conference);
            UsersGetResponse usersGetResponse = new UsersGetResponse(userList);
            usersGetResponse.setMessage(SUCCESS);
            return responseWrapperUtil.
                    buildResponseBody(usersGetResponse, HttpStatus.OK);
        }
    }


    @PreAuthorize("hasAuthority('USER') and hasPermission(null, 'conference:chair:'+#request.id)")
    public ResponseEntity<?> invitePcMembers(PcMemberInviteRequest request) {
        Conference conference = conferenceService.findById(request.getId());
        Collection<String> candidateList = request.getUsernames();

        boolean isArgumentsCorrect = pcMemberInviteService.process(conference, candidateList);

        BaseResponse baseResponse;

        if (!isArgumentsCorrect) {
            baseResponse = new BaseResponse(ARGUMENTS_ERROR);
            return responseWrapperUtil.buildResponseBody(baseResponse, HttpStatus.NOT_ACCEPTABLE);
        } else {
            baseResponse = new BaseResponse(SUCCESS);
            return responseWrapperUtil.buildResponseBody(baseResponse, HttpStatus.OK);
        }
    }

    @PreAuthorize("hasAuthority('USER') and hasPermission(null, 'conference:chair:'+#request.id)")
    public ResponseEntity<?> openSubmission(OnlyIdPropertyRequest request) {
        Conference conference = conferenceService.findById(request.getId());

        BaseResponse baseResponse;

        if (conference == null || !conference.getStage().equals(ConferenceStage.PREPARING)
                || !conference.getState().equals(ConferenceState.PASSED)) {
            baseResponse = new BaseResponse(ARGUMENTS_ERROR);
            return responseWrapperUtil.buildResponseBody(baseResponse, HttpStatus.NOT_ACCEPTABLE);
        } else if (!dateUtils.isLeftOutOfRightBorder(conference.getDeadline(), new Date())) {
            baseResponse = new BaseResponse("conflict");
            return responseWrapperUtil.buildResponseBody(baseResponse, HttpStatus.CONFLICT);
        } else {
            conferenceService.changeConferenceStage(conference, ConferenceStage.CONTRIBUTION_START);
            baseResponse = new BaseResponse(SUCCESS);
            return responseWrapperUtil.buildResponseBody(baseResponse, HttpStatus.OK);
        }
    }

    @PreAuthorize("hasAuthority('USER') and hasPermission(null, 'conference:chair:'+#request.id)")
    public ResponseEntity<?> startReview(ReviewStartRequest request) {

        Conference conference = conferenceService.findById(request.getId());
        int strategy = request.getStrategy();
        AllocationStrategy allocationStrategy;
        if (strategy < 0 || strategy > 1) {
            BaseResponse baseResponse = new BaseResponse(ARGUMENTS_ERROR);
            return responseWrapperUtil.buildResponseBody(baseResponse, HttpStatus.NOT_ACCEPTABLE);
        }
        allocationStrategy = AllocationStrategy.values()[strategy];
        conference.setStrategy(allocationStrategy);
        if (!paperAllocateService.process(conference)) {
            BaseResponse baseResponse = new BaseResponse("failed: check papers and pcMembers plz.");
            return responseWrapperUtil.buildResponseBody(baseResponse, HttpStatus.CONFLICT);
        } else {
            BaseResponse baseResponse = new BaseResponse(SUCCESS);
            return responseWrapperUtil.buildResponseBody(baseResponse, HttpStatus.OK);
        }
    }

    @PreAuthorize("hasAuthority('USER') and hasPermission(null, 'conference:chair:'+#request.id)")
    public ResponseEntity<?> releaseResult(OnlyIdPropertyRequest request) {

        Conference conference = conferenceService.findById(request.getId());
        if (!conference.getStage().equals(ConferenceStage.REVIEW_END)) {
            return responseWrapperUtil.buildBaseResponseBody(
                    ARGUMENTS_ERROR,
                    HttpStatus.NOT_ACCEPTABLE);
        } else {
            if (0 != reviewInfoService.numberOfReviewInfos(request.getId(), 0)) {
                return responseWrapperUtil.buildBaseResponseBody(
                        "not all PcMembers confirm", HttpStatus.METHOD_NOT_ALLOWED);
            }
            conferenceService.changeConferenceStage(conference, ConferenceStage.RESULT_RELEASE);
            return responseWrapperUtil.buildBaseResponseBody(
                    SUCCESS,
                    HttpStatus.OK);
        }
    }

    @PreAuthorize("hasAuthority('USER') and hasPermission(null, 'conference:chair:'+#request.id)")
    public ResponseEntity<?> modifyDates(DatesModifyRequest request) {

        Conference conference = conferenceService.findById(request.getId());
        BaseResponse baseResponse;
        if (conferenceDatesModifyService.process(conference, request.getDateSet())) {
            baseResponse = new BaseResponse(SUCCESS);
            return responseWrapperUtil.buildResponseBody(baseResponse, HttpStatus.OK);
        } else {
            baseResponse = new BaseResponse(ARGUMENTS_ERROR);
            return responseWrapperUtil.buildResponseBody(baseResponse, HttpStatus.NOT_ACCEPTABLE);
        }
    }

    @PreAuthorize("hasAuthority('USER') and hasPermission(null, 'conference:chair:'+#request.id)")
    public ResponseEntity<?> releaseFinal(OnlyIdPropertyRequest request) {
        Conference conference = conferenceService.findById(request.getId());
        if (!conference.getStage().equals(ConferenceStage.RESULT_RELEASE)) {
            return responseWrapperUtil.buildBaseResponseBody(
                    ARGUMENTS_ERROR,
                    HttpStatus.NOT_ACCEPTABLE);
        } else {
            if (0 != reviewInfoService.numberOfRebuttedReviewInfos(request.getId(), 1)) {
                return responseWrapperUtil.buildBaseResponseBody(
                        "not all PcMembers confirm rebuttals", HttpStatus.METHOD_NOT_ALLOWED);
            }
            reviewCopyService.substituteReviewInfos(conference);
            conference = conferenceService.findById(request.getId());
            conferenceService.changeConferenceStage(conference, ConferenceStage.FINAL_RELEASE);
            return responseWrapperUtil.buildBaseResponseBody(
                    SUCCESS,
                    HttpStatus.OK);
        }

    }
}
